//
// Created by fairy on 2025/4/28 10:44.
//
#ifndef AIC23_H
#define AIC23_H
#include <zq_conf.h>
#include <zq_i2c.h>
#include <zq_mcbsp.h>
#include <zq_systick.h>
#include <zq_dma.h>


namespace bsp
{
    namespace aic23
    {
        namespace detail
        {
            // 寄存器地址
            DECLARE_ATTRIBUTE(REG,
                              LT_LINE_CTL = 0x00, // 0 左线性输入声道音量控制
                              RT_LINE_CTL = 0x02, // 1 右线性输入声道音量控制
                              LT_HP_CTL = 0x04, // 2 左耳机输出声道音量控制
                              RT_HP_CTL = 0x06, // 3 右耳机输出声道音量控制
                              ANALOG_AUDIO_CTL = 0x08, // 4 模拟音频通道控制
                              DIGITAL_AUDIO_CTL= 0x0A, // 5 数字音频通道控制
                              POWER_DOWN_CTL = 0x0C, // 6 电源控制
                              DIGITAL_IF_FORMAT= 0x0E, // 7 数字音频接口格式
                              SAMPLE_RATE_CTL = 0x10, // 8 采样率控制
                              DIG_IF_ACTIVATE = 0x12, // 9 数字接口激活
                              RESET=0x1E // F 复位寄存器
            )

            // 数字音频接口格式控制寄存器 (DIGIF_FMT)
            DECLARE_ATTRIBUTE(DIGIF_FMT,
                              // 接口模式控制
                              MS = 0x40, // 主从模式选择 (0: 从模式，1: 主模式)
                              LRSWAP = 0x20, // 左右声道交换 (0: 正常，1: 交换)
                              LRP = 0x10, // LR相位控制 (0: 正常相位，1: 反转相位)

                              // 输入字长配置 (IWL)
                              IWL = 0x0C, // 数据位宽选择：
                              IWL_16 = 0x00, // 16位数据格式
                              IWL_20 = 0x04, // 20位数据格式
                              IWL_24 = 0x08, // 24位数据格式
                              IWL_32 = 0x0C, // 32位数据格式

                              // 数据格式选择 (FOR)
                              FOR = 0x03, // 音频接口协议：
                              FOR_MSBRIGHT = 0x00, // 右对齐MSB格式
                              FOR_MSLEFT = 0x01, // 左对齐MSB格式
                              FOR_I2S = 0x02, // I2S标准格式
                              FOR_DSP = 0x03 // DSP模式(带帧同步)
            )

            // 电源管理控制寄存器 (POWER)
            DECLARE_ATTRIBUTE(POWER,
                              DEV = 0x80, // 总设备电源 (1: 开启)
                              CLK = 0x40, // 时钟系统电源 (1: 开启)
                              OSC = 0x20, // 振荡器电源 (1: 开启)
                              OUT = 0x10, // 输出级电源 (1: 开启)
                              DAC = 0x08, // DAC模块电源 (1: 开启)
                              ADC = 0x04, // ADC模块电源 (1: 开启)
                              MIC = 0x02, // 麦克风偏置电压 (1: 使能)
                              LINE = 0x01 // 线路输入电源 (1: 开启)
            )

            // 采样率控制寄存器 (SRC)
            DECLARE_ATTRIBUTE(SRC,
                              CLKOUT = 0x80, // 时钟输出使能 (1: 输出时钟)
                              CLKIN = 0x40, // 时钟输入选择 (0: 内部时钟，1: 外部时钟)

                              // 采样率配置 (SR)
                              SR = 0x3C, // 主采样率设置：
                              SR_44 = 0x20, // 44.1kHz 采样率
                              SR_32 = 0x18, // 32kHz 采样率

                              BOSR = 0x02, // 超采样率选择 (0: 250fs，1: 272fs)
                              MO = 0x01 // 主时钟分频器 (0: 256分频，1: 384分频)
            )

            // 模拟音频路径控制寄存器 (ANAPCTL)
            DECLARE_ATTRIBUTE(ANAPCTL,
                              STA = 0xC0, // ADC状态机控制：
                              STA_IDLE = 0x00, // 空闲状态
                              STA_RUN = 0x40, // 运行状态
                              STA_STOP = 0x80, // 停止状态

                              STE = 0x20, // ADC自检使能 (1: 启用自检)
                              DAC = 0x10, // DAC旁路控制 (0: 正常，1: 旁路)
                              BYP = 0x08, // 输入旁路 (1: 绕过模拟混合器)
                              INSEL = 0x04, // 输入选择 (0: 线路输入，1: 麦克风输入)
                              MICM = 0x02, // 麦克风增益 (0: 0dB，1: +20dB)
                              MICB = 0x01 // 麦克风偏置 (1: 启用偏置电压)
            )

            // 数字音频路径控制寄存器 (DIGPCTL)
            DECLARE_ATTRIBUTE(DIGPCTL,
                              DACM = 0x08, // DAC主模式 (1: 主模式，0: 从模式)
                              DEEMP = 0x06, // 去加重滤波器选择：
                              DEEMP_DIS = 0x00, // 禁用去加重
                              DEEMP_32 = 0x02, // 32kHz 去加重
                              DEEMP_44 = 0x04, // 44.1kHz 去加重
                              DEEMP_48 = 0x06, // 48kHz 去加重
                              ADCHP = 0x01 // ADC高通滤波 (1: 启用)
            )

            // 数字接口激活控制寄存器 (DIGIFACT)
            DECLARE_ATTRIBUTE(DIGIFACT,
                              ACT = 0x01 // 接口激活控制 (1: 启用数字接口)
            )
        }
    }
}

namespace bsp
{
    /**
     * @输入模式 一共分为麦克风输入和立体声输入，通过配置ANALOG_AUDIO_CTL寄存器实现
     *      ①麦克风输入，输入通道为单声道MICIN（drr1和drr2的值相同），还有一个引脚为MICBIAS，是偏置输出引脚，通过初始化过程时配置对应寄存器使能（2.5V，不使能则没有）
     *      ②立体声输入，输入通道为左右双声道，拥有更宽的频率响应和更纯净的信号路径，适合示波器应用（选择单声道并把增益设置为0即可，另一路最好设置为静音，即0x80）
     *
     */
    namespace aic23
    {
        class Control
        {
            // TO DO 得再写一个类，专门用于操作变量，从而设置寄存器的不同位
            static void write_cmd(const unsigned short reg_addr, const unsigned short data)
            {
                zq::I2C::send(0x1A, reg_addr, data);
            }

        public:
            // 使用USB模式 MCLK=12MHz的情况下 bits[5:3] + bit[2]
            DECLARE_ATTRIBUTE(SAMPLE_RATE,
                              SR_96K = ((0x07)<<1) | 0x00, // 0b0111 0 96K
                              SR_88K = ((0x0F)<<1) | 0x01, // 0b1111 1 88.2K
                              SR_48K = ((0x00)<<1) | 0x00, // 0b0000 0 48K
                              SR_44K = ((0x08)<<1) | 0x01, // 0b1000 1 44.1K
                              SR_32K = ((0x06)<<1) | 0x00, // 0b0011 0 32K
                              SR_8_021K = ((0x0B)<<1) | 0x01, // 0b1011 1 8.021K
                              SR_8K = ((0x03)<<1) | 0x00, // 0b0011 0 8K        // 不知道为什么，不可以正常使用，其他的都可以
            )

            /**
             *
             * @param receive_IT true表示立即开启接收中断
             * @param transmit_IT true表示立即开启发送中断
             */
            template<SAMPLE_RATE::Type sample_rate>
            static void init(const bool receive_IT = false, const bool transmit_IT = false)
            {
                // 初始化I2C
                zq::i2c::Config cfg;
                cfg.system_clock = 192000000;
                cfg.bitrate = 400000;
                cfg.loopback = false;
                zq::I2C::init(cfg);

                // 初始化AIC23寄存器
                write_cmd(detail::REG::RESET, 0); //复位寄存器
                write_cmd(detail::REG::POWER_DOWN_CTL, 0); //所有电源都打开
                // write_cmd(detail::REG::ANALOG_AUDIO_CTL, detail::ANAPCTL::DAC | detail::ANAPCTL::INSEL); //打开DAC 选择传声器 选择麦克风输入(MICIN)
                write_cmd(detail::REG::ANALOG_AUDIO_CTL, detail::ANAPCTL::DAC); //打开DAC 选择传声器 选择线输入
                write_cmd(detail::REG::DIGITAL_AUDIO_CTL, 0); //数字音频通道控制   禁止去加重

                // 打开线入声道音量
                write_cmd(detail::REG::LT_LINE_CTL, 0x01F); //左声道输入衰减正常，最小为-34.5dB
                write_cmd(detail::REG::RT_LINE_CTL, 0x01F); //右声道输入衰减正常，最小为-34.5dB  禁止左右声道同时更新

                // 数字音频接口主模式    输入长度16位     DSP初始化
                // 采样率控制 44.1KHz 比较常用   SRC_BOSR为272fs  USB clock
                write_cmd(detail::REG::DIGITAL_IF_FORMAT,
                          detail::DIGIF_FMT::MS | detail::DIGIF_FMT::IWL_16 | detail::DIGIF_FMT::FOR_DSP);

                // 理论上0x01是48KHz，0x1D是96KHz,0x23是44.1KHz
                // write_cmd(detail::REG::SAMPLE_RATE_CTL, 0x1D);
                set_sample_rate(sample_rate);

                // 打开耳机声道音量和数字接口激活
                write_cmd(detail::REG::LT_HP_CTL, 0x1ff); //激活 衰减+6dB 零点检测  开启
                write_cmd(detail::REG::RT_HP_CTL, 0x1ff);
                write_cmd(detail::REG::DIG_IF_ACTIVATE, detail::DIGIFACT::ACT);

                // 初始化McBSP
                zq::mcbsp::Control::init(receive_IT, transmit_IT);
            }

            template<typename DMAx>
            struct BaseDMA
            {
                struct circleLoop
                {
                    static void enable() { DMAx::config::circleLoop::enable(); }

                    static void disable() { DMAx::config::circleLoop::disable(); }
                };

                // ================= 优先级配置 =================
                struct priority
                {
                    // 设置通道为高优先级
                    static void high() { DMAx::config::priority::high(); }

                    // 设置通道为低优先级
                    static void low() { DMAx::config::priority::low(); }
                };
            };

            template<typename DMAx>
            struct receiveDMA : BaseDMA<DMAx>
            {
                static void init()
                {
                    DMAx::config::mode::peripheralToMemory();// 使用预设
                }

                template<typename bufPtrType>
                static void start(bufPtrType buf_ptr, uint32_t size)
                {
                    // 启动DMA传输
                    DMAx::start(
                        zq::dma::detail::info::Event::McBSP0_Receive,   // 同步事件
                        zq::mcbsp::regs::drr1::REG,                     // McBSP接收寄存器字节地址
                        reinterpret_cast<uint32_t>(buf_ptr),        // 缓冲区字节地址
                        size                                    // 元素数量
                    );
                }

                static void restart()
                {
                    DMAx::restart();
                }

                static bool isComplete()
                {
                    return DMAx::getITFlags(DMAx::IT_Flags::blockIE);
                }
            };

            template<typename DMAx>
            struct transmitDMA : BaseDMA<DMAx>
            {
                /**
                 * 初始化DMA
                 * @tparam is_circle 是否循环发送
                 * @tparam is_dram 传输的源地址是否是位于DRAM
                 */
                template<bool is_circle, bool is_dram>
                static void init()
                {
                    DMAx::config::mode::memoryToPeripheral(false);// 不启用块中断
                    if (is_dram)
                        DMAx::config::srcPort::dram();  // 内存为DARAM
                    if (is_circle)
                        DMAx::config::circleLoop::enable();// 使用循环模式
                }

                template<typename bufPtrType>
                static void start(bufPtrType buf_ptr, uint32_t size)
                {
                    DMAx::start(
                        zq::dma::detail::info::Event::McBSP0_Transmit,   // 同步事件：McBSP发送
                        reinterpret_cast<uint32_t>(buf_ptr),             // 源地址：波表
                        zq::mcbsp::regs::dxr1::REG,                      // 目标地址：McBSP发送寄存器
                        size                                             // 元素数量
                    );
                    write::data(0xFFFF, 0xFFFF);
                }
            };


            struct read
            {
                /**
                  * 读取通道1的数据
                  * @param buf_1 通道1，对于线输入来说是左声道
                  * @param size 数组的大小
                  * @note 使用效果很好，我很满意
                  */
                static void buf_1(short buf_1[], const uint16_t size)
                {
                    for (uint16_t i = 0; i < size; ++i)
                    {
                        zq::mcbsp::Control::read_data1(buf_1[i]);
                    }
                }

                /**
                 * 读取通道1的数据
                 * @param data 通道1，对于线输入来说是左声道
                 * @note 想要连续触发接收中断，必须在接收中断（或者别的地方）里继续读取drr1寄存器。因为只有读取数据寄存器才能激活下一次的接收中断
                 */
                static void data1_IT(short &data)
                {
                    zq::mcbsp::Control::read_data1_IT(data);
                }


                /**
                 * 读取通道1的数据
                 * @param buf_2 通道2，对于线输入来说是右声道
                 * @param size 数组的大小
                 * @warning 使用效果不咋样，因为读取的数据与预期不一样。在麦克风输入的情况下，理应与通道1保持一致，但事实是，偶尔可以达到预期效果，
                 *          并且多见于read_buf函数。原因未知，且不好推测原因
                 */
                static void buf_2(short buf_2[], const uint16_t size)
                {
                    for (uint16_t i = 0; i < size; ++i)
                    {
                        zq::mcbsp::Control::read_data2(buf_2[i]);
                    }
                }

                /**
                 * 同时读取两个通道的数据
                 * @param buf_1 通道1，对于线输入来说是左声道
                 * @param buf_2 通道2，对于线输入来说是右声道
                 * @param size 数组的大小
                 * @warning 对于高采样率如44.1KHz需慎用，因为同时读取两个通道的数据可能会导致实际采样率达不到要求，建议使用read_buf_1函数
                 */
                static void buf(short buf_1[], short buf_2[], const uint16_t size)
                {
                    for (uint16_t i = 0; i < size; ++i)
                    {
                        zq::mcbsp::Control::read(buf_1[i], buf_2[i]);
                    }
                }
            };

            struct write
            {
                static void data1_IT(const short data1, const short data2)
                {
                    zq::mcbsp::Control::write_IT(data1, data2);
                }

                static void buf(const short buf_L[], const short buf_R[], const uint16_t size)
                {
                    for (uint16_t i = 0; i < size; ++i)
                    {
                        zq::mcbsp::Control::write(buf_L[i], buf_R[i]);
                    }
                }

                static void data(const short buf_L, const short buf_R)
                {
                    zq::mcbsp::Control::write(buf_L, buf_R);
                }
            };


            // 设置采样率(动态改变不太现实，只有前两次可以，后面再改就会导致任何波形都无法产生)
            static void set_sample_rate(const SAMPLE_RATE::Type sample_rate)
            {
                write_cmd(detail::REG::SAMPLE_RATE_CTL, sample_rate << 1 | 0x01); // USB模式
            }
        };
    }
}
#endif //AIC23_H
